package com.wontube.core.conf;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.wontube.core.cron.JobsManager;
import com.wontube.core.srv.schedule.IScheduleTask;


/**
 * @author yuechao
 * 
 * 分 时 日 月  星期
 * 
 */
public final class Cron implements IScheduleTask, Serializable {
	private static final long serialVersionUID = 5402066032562448688L;
	private static final Logger logger = LoggerFactory.getLogger(Cron.class);
	private final Object syncRoot = new Object();
	
	private int[] weeks  = new int[0];
	private int[] monthes = new int[0];
	private int[] dates = new int[0];
    private int[] hrs = new int[0];
    private int[] mins = new int[0];

    private String command;
    private boolean isAsync = true;
    private String[] arguments = new String[0];
    
    private Date lastExcuted;
    
    public Cron() {
    	Calendar calendar = Calendar.getInstance();
    	calendar.add(Calendar.MINUTE, -5);
    	this.lastExcuted = calendar.getTime();
    }
	/* 
	 * @see core.srv.schedule.ITask#execute()
	 */
	@Override
	public void execute(Date time) {
		synchronized (this.syncRoot) { // 判断时间必须同步，因为是多线程执行
	    	Calendar calendar = Calendar.getInstance();
	    	calendar.setTime(time);
	    	
	    	// 刚执行过不到1分钟，不需要执行
	    	calendar.add(Calendar.MINUTE, -1);
	    	if (this.lastExcuted.after(calendar.getTime())) {
	    		return;
	    	}
	    	
	    	this.lastExcuted = time;
		}
		
		// 注意，这里必须使用广播时间，因为系统时间会比广播时间延后
		JobsManager.execute(this);
	}
	
	@Override
	public boolean isAsync() {
		return this.isAsync;
	}
	
	@Override
    public boolean shouldExcute(Date time) {
    	// 分钟必须配置
    	if (this.mins.length < 1) {
    		return false;
    	}
    	
    	Calendar calendar = Calendar.getInstance();
    	calendar.setTime(time);

    	// 分钟是否匹配
    	if (Arrays.binarySearch(this.mins, calendar.get(Calendar.MINUTE)) < 0) {
    		return false;
    	}
    	
    	// 星期是否匹配
    	boolean isWeekMatch = this.weeks.length < 1 || Arrays.binarySearch(this.weeks, calendar.get(Calendar.DAY_OF_WEEK)) >= 0;
    	
    	// 日期和月份是否匹配
    	if ((this.monthes.length > 0 && Arrays.binarySearch(this.monthes, calendar.get(Calendar.MONTH)) < 0)
    			|| (this.dates.length > 0 && Arrays.binarySearch(this.dates, calendar.get(Calendar.DATE)) < 0)) {
    		if (!isWeekMatch) {
    			return false;
    		}
    	}

    	// 小时是否匹配
    	if (this.hrs.length > 0 && Arrays.binarySearch(this.hrs, calendar.get(Calendar.HOUR_OF_DAY)) < 0) {
    		return false;
    	}
    	
    	return true;
    }
    
    public boolean parse(String text) {
    	if ("".equals(text)) {
    		return false;
    	}
    	
    	String[] args = text.split("\\s+");
    	if (args.length < 7) {
    		logger.error("error cron config");
    		return false;
    	}

    	if ("*".equals(args[0])) {
    		logger.error("must specify minite.");
    		return false;
    	}
    	
    	String[] ts = args[0].split("[,]");
    	this.mins = new int[ts.length];
    	for (int i = 0; i < ts.length; i++) {
    		this.mins[i] = Integer.parseInt(ts[i]);
    	}
    	
    	ts = new String[0];
    	if (!"*".equals(args[1])) {
    		ts = args[1].split("[,]");
    	}
    	this.hrs = new int[ts.length];
    	for (int i = 0; i < ts.length; i++) {
    		this.hrs[i] = Integer.parseInt(ts[i]);
    	}
    	
    	ts = new String[0];
    	if (!"*".equals(args[2])) {
    		ts = args[2].split("[,]");
    	}
    	this.dates = new int[ts.length];
    	for (int i = 0; i < ts.length; i++) {
    		this.dates[i] = Integer.parseInt(ts[i]);
    	}
    	
    	ts = new String[0];
    	if (!"*".equals(args[3])) {
    		ts = args[3].split("[,]");
    	}
    	this.monthes = new int[ts.length];
    	for (int i = 0; i < ts.length; i++) {
    		this.monthes[i] = Integer.parseInt(ts[i]);
    	}
    	
    	ts = new String[0];
    	if (!"*".equals(args[4])) {
    		ts = args[4].split("[,]");
    	}
    	this.weeks = new int[ts.length];
    	for (int i = 0; i < ts.length; i++) {
    		this.weeks[i] = Integer.parseInt(ts[i]);
    	}

    	this.isAsync = !"-sync".equals(args[5]);
    	this.command = args[6];
		this.arguments = new String[args.length - 7];
    	for (int i = 7; i < args.length; i++) {
    		this.arguments[i - 7] = args[i];
    	}

    	return true;
    }
    
	public int[] getWeeks() {
		return this.weeks;
	}
	
	public void setWeeks(int[] value) {
		this.weeks = value;
	}
	
	public int[] getMonthes() {
		return this.monthes;
	}
	
	public void setMonthes(int[] monthes) {
		this.monthes = monthes;
	}
	public int[] getDates() {
		return dates;
	}
	
	public void setDates(int[] dates) {
		this.dates = dates;
	}
	
	public int[] getHrs() {
		return this.hrs;
	}
	
	public void setHrs(int[] hrs) {
		this.hrs = hrs;
	}
	
	public int[] getMins() {
		return this.mins;
	}
	
	public void setMins(int[] mins) {
		this.mins = mins;
	}
	
	public String getCommand() {
		return this.command;
	}
	
	public void setCommand(String value) {
		this.command = value;
	}
	
	public String[] getArguments() {
		return this.arguments;
	}
	
	public void setArguments(String[] value) {
		this.arguments = value;
	}
	
	public String getArgsString() {
		StringBuilder sb = new StringBuilder();
		sb.append("[");
		for (int i = 0; i < this.arguments.length; i++) {
			if (i > 0) {
				sb.append(",");
			}
			sb.append(this.arguments[i]);
		}
		sb.append("]");
		
		return sb.toString();
	}
	
    public Date getLastExecuted() {
    	return this.lastExcuted;
    }
    
    public void setExecuted(Date value) {
    	this.lastExcuted = value;
    }
}
